package com.hwd.gmall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hwd.gmall.common.constant.CacheConst;
import com.hwd.gmall.model.product.*;
import com.hwd.gmall.product.mapper.*;
import com.hwd.gmall.product.service.ItemService;
import lombok.extern.log4j.Log4j2;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author 黄伟东/Victor
 * @date 2022/4/22 10:40
 */
@Service
@Log4j2
public class ItemServiceImpl implements ItemService {

    @Resource
    private SkuInfoMapper skuInfoMapper;

    @Resource
    private BaseCategoryViewMapper baseCategoryViewMapper;

    @Resource
    private SkuImageMapper skuImageMapper;

    @Resource
    private SpuSaleAttrMapper spuSaleAttrMapper;

    @Resource
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private BaseTrademarkMapper baseTrademarkMapper;

    @Resource
    private BaseAttrInfoMapper baseAttrInfoMapper;

    /**
     * 根据skuId获取skuInfo信息
     *
     * @param skuId skuInfo表的id
     * @return com.hwd.gmall.model.product.SkuInfo
     */
    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        // 查询skuInfo的信息
        return skuInfoMapper.selectById(skuId);
    }

    /**
     * 根据skuId获取skuInfo信息
     *
     * @param skuId skuInfo表的id
     * @return com.hwd.gmall.model.product.SkuInfo
     */
    @Override
    public SkuInfo getSkuInfoFromRedisOrDb(Long skuId) {

        String key = String.format("Sku:%d:info", skuId);
        // 从 redis 中获取商品的数据
        SkuInfo skuInfo =
                (SkuInfo) redisTemplate.opsForValue().get(key);
        // redis中有数据，直接返回
        if (skuInfo != null) {
            return skuInfo;
        }
        // 如果redis没有数据，获取锁
        RLock lock = redissonClient.getLock(String.format("Sku:%d:lock", skuId));
        try {
            // 尝试加锁，
            if (lock.tryLock(CacheConst.LOCK_WAIT_TIME, CacheConst.LOCK_LEASE_TIME, TimeUnit.SECONDS)) {
                try {
                    // 加锁成功进行数据库查询
                    skuInfo = skuInfoMapper.selectById(skuId);
                    if (skuInfo == null || skuInfo.getId() == null) {
                        // 数据库也没有数据，缓存空，并设置 5 分钟过期时间（解决缓存穿透问题）
                        skuInfo = new SkuInfo();
                        redisTemplate.opsForValue().set(key, skuInfo, 5, TimeUnit.MINUTES);
                    } else {
                        // redis中没有数据，数据库中有，把查询到的数据存到redis中，并设置 24 小时过去时间
                        redisTemplate.opsForValue().set(key, skuInfo, 24, TimeUnit.HOURS);
                    }
                    // 返回结果
                    return skuInfo;
                } catch (Exception e) {
                    log.error("加锁成功，代码逻辑出现异常，异常信息是：" + e.getMessage());
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            log.error("加锁失败，出现的异常信息是：" + e.getMessage());
        }

        return skuInfo;
    }

    /**
     * 根据第三级分类id查询所有分类的信息（包括一级、二级分类信息）
     *
     * @param category3Id 三级分类id
     * @return com.hwd.gmall.model.product.BaseCategoryView
     */
    @Override
    public BaseCategoryView getCategory(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }

    /**
     * 根据skuIf查询sku的图片列表
     *
     * @param skuId skuInfo表的id
     * @return java.util.List<com.hwd.gmall.model.product.SkuImage>
     */
    @Override
    public List<SkuImage> listSkuImages(Long skuId) {
        return skuImageMapper.selectList(
                new LambdaQueryWrapper<SkuImage>()
                        .eq(SkuImage::getSkuId, skuId));
    }

    /**
     * 根据spuId和skuId查询商品的销售属性
     *
     * @param spuId spuId
     * @param skuId skuId
     * @return java.util.List<com.hwd.gmall.model.product.SpuSaleAttr>
     */
    @Override
    public List<SpuSaleAttr> listSpuSaleAttrBySpuIdAndSkuId(Long spuId, Long skuId) {
        return spuSaleAttrMapper.selectSpuSaleAttrBySpuIdAndSkuId(spuId, skuId);
    }


    /**
     * 根据skuId查询sku的价格
     *
     * @param skuId sku的id
     * @return java.math.BigDecimal
     */
    @Override
    public BigDecimal getSkuPrice(Long skuId) {

        // 根据skuId查询sku的价格
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);

        // 检验商品是否存在
        if (skuInfo == null || skuInfo.getId() == null) {
            throw new RuntimeException("商品不存在！");
        }
        return skuInfo.getPrice();
    }

    /**
     * 查询指定spu的所有销售属性值与skuId的键值对
     *
     * @param spuId spuId
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.String>>
     */
    @Override
    public Map<String, String> getSkuIdsValueIdsMap(Long spuId) {

        //查询所有的键值对
        List<Map<Object, Object>> maps = skuSaleAttrValueMapper.getSkuSaleAttrValueInfo(spuId);

        // 返回结果集初始化
        Map<String, String> result = new ConcurrentHashMap<>(16);

        //遍历处理
        maps.stream().forEach(map ->
                result.put(
                        map.get("values_id").toString(),
                        map.get("sku_id").toString()));

        return result;
    }

    /**
     * 根据品牌id查询品牌信息
     *
     * @param id 品牌表id
     * @return com.hwd.gmall.model.product.BaseTrademark
     */
    @Override
    public BaseTrademark getBaseTrademark(Long id) {
        return baseTrademarkMapper.selectById(id);
    }

    /**
     * 根据skuId查询商品的平台属性名称和值信息
     *
     * @param skuId shuInfo表的id
     * @return java.util.List<com.hwd.gmall.model.product.BaseAttrInfo>
     */
    @Override
    public List<BaseAttrInfo> getBaseAttrInfo(Long skuId) {
        return baseAttrInfoMapper.selectBaseAttrInfoBySkuId(skuId);
    }

    /**
     * 扣减库存
     *
     * @param stockDeductMap 扣减的商品id和数量的map对象
     * @return java.lang.Boolean
     */
    @Override
    public Boolean deductStock(Map<String, Object> stockDeductMap) {
        try {
            // 遍历获取每个扣除的商品的数据
            stockDeductMap.entrySet().stream().forEach(entry -> {
                // 扣减库存
                skuInfoMapper.deductStock(
                        Long.parseLong(entry.getKey()),
                        Integer.parseInt(entry.getValue().toString()));
            });
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 库存回滚
     *
     * @param stockRollbackMap 回滚的商品id和数量的map对象
     * @return java.lang.Boolean
     */
    @Override
    public Boolean rollbackStock(Map<String, Object> stockRollbackMap) {
        try {
            // 遍历获取每个回滚的商品的数据
            stockRollbackMap.entrySet().stream().forEach(entry -> {
                // 回滚库存
                skuInfoMapper.rollBackStock(
                        Long.parseLong( entry.getKey()),
                        Integer.parseInt(entry.getValue().toString()));
            });
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}
